Don't use flock on NFS mounts
authorAlex Crichton <alex@alexcrichton.com>
Tue, 26 Apr 2016 22:58:49 +0000 (15:58 -0700)
committerAlex Crichton <alex@alexcrichton.com>
Tue, 26 Apr 2016 22:58:49 +0000 (15:58 -0700)
Completely skip file locking when we detect an NFS mount via `statfs`.

Closes #2615

src/cargo/util/flock.rs

index 98a27672368e71daa3399c96b70ef9a2009ebea1..9f6a5352a197b8c7d7a14c03d2c1b77a679e74d6 100644 (file)
@@ -250,6 +250,21 @@ fn acquire(config: &Config,
            path: &Path,
            try: &Fn() -> io::Result<()>,
            block: &Fn() -> io::Result<()>) -> CargoResult<()> {
+
+    // File locking on Unix is currently implemented via `flock`, which is known
+    // to be broken on NFS. We could in theory just ignore errors that happen on
+    // NFS, but apparently the failure mode [1] for `flock` on NFS is **blocking
+    // forever**, even if the nonblocking flag is passed!
+    //
+    // As a result, we just skip all file locks entirely on NFS mounts. That
+    // should avoid calling any `flock` functions at all, and it wouldn't work
+    // there anyway.
+    //
+    // [1]: https://github.com/rust-lang/cargo/issues/2615
+    if is_on_nfs_mount(path) {
+        return Ok(())
+    }
+
     match try() {
         Ok(()) => return Ok(()),
         Err(e) => {
@@ -263,9 +278,34 @@ fn acquire(config: &Config,
     let msg = format!("waiting for file lock on {}", msg);
     try!(config.shell().err().say_status("Blocking", &msg, CYAN, true));
 
-    block().chain_error(|| {
+    return block().chain_error(|| {
         human(format!("failed to lock file: {}", path.display()))
-    })
+    });
+
+    #[cfg(target_os = "linux")]
+    fn is_on_nfs_mount(path: &Path) -> bool {
+        use std::ffi::CString;
+        use std::mem;
+        use std::os::unix::prelude::*;
+        use libc;
+
+        let path = match CString::new(path.as_os_str().as_bytes()) {
+            Ok(path) => path,
+            Err(_) => return false,
+        };
+
+        unsafe {
+            let mut buf: libc::statfs = mem::zeroed();
+            let r = libc::statfs(path.as_ptr(), &mut buf);
+
+            r == 0 && buf.f_type == libc::NFS_SUPER_MAGIC
+        }
+    }
+
+    #[cfg(not(target_os = "linux"))]
+    fn is_on_nfs_mount(_path: &Path) -> bool {
+        false
+    }
 }
 
 fn create_dir_all(path: &Path) -> io::Result<()> {